
<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <title>12.8.&nbsp; SOAP 网络服务故障排除</title>
      <link rel="stylesheet" href="../diveintopython.css" type="text/css">
      <link rev="made" href="mailto:f8dy@diveintopython.org">
      <meta name="generator" content="DocBook XSL Stylesheets V1.52.2">
      <meta name="keywords" content="Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free">
      <meta name="description" content="Python from novice to pro">
      <link rel="home" href="../toc/index.html" title="Dive Into Python">
      <link rel="up" href="index.html" title="第&nbsp;12&nbsp;章&nbsp;SOAP Web 服务">
      <link rel="previous" href="google.html" title="12.7.&nbsp;搜索 Google">
      <link rel="next" href="summary.html" title="12.9.&nbsp;小结">
   </head>
   <body>
      <table id="Header" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td id="breadcrumb" colspan="5" align="left" valign="top">导航：<a href="../index.html">起始页</a>&nbsp;&gt;&nbsp;<a href="../toc/index.html">Dive Into Python</a>&nbsp;&gt;&nbsp;<a href="index.html">SOAP Web 服务</a>&nbsp;&gt;&nbsp;<span class="thispage"> SOAP 网络服务故障排除</span></td>
            <td id="navigation" align="right" valign="top">&nbsp;&nbsp;&nbsp;<a href="google.html" title="上一页: “搜索 Google”">&lt;&lt;</a>&nbsp;&nbsp;&nbsp;<a href="summary.html" title="下一页: “小结”">&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3" id="logocontainer">
               <h1 id="logo"><a href="../index.html" accesskey="1">深入 Python :Dive Into Python 中文版</a></h1>
               <p id="tagline">Python 从新手到专家 [Dip_5.4b_CPyUG_Release]</p>
            </td>
            <td colspan="3" align="right">
               <form id="search" method="GET" action="http://www.google.com/custom">
                  <p><label for="q" accesskey="4">Find:&nbsp;</label><input type="text" id="q" name="q" size="20" maxlength="255" value=""> <input type="submit" value="搜索"><input type="hidden" name="domains" value="woodpecker.org.cn/diveintopython"><input type="hidden" name="sitesearch" value="www.woodpecker.org.cn/diveintopython"></p>
               </form>
            </td>
         </tr>
      </table>
      <!--#include virtual="/inc/ads" -->
      <div class="section" lang="zh_cn">
         <div class="titlepage">
            <div>
               <div>
                  <h2 class="title"><a name="soap.troubleshooting"></a>12.8.&nbsp; <span class="acronym">SOAP</span> 网络服务故障排除
                  </h2>
               </div>
            </div>
            <div></div>
         </div>
         <div class="abstract">
            <p>是的，<span class="acronym">SOAP</span> 网络服务的世界中也不总是欢乐和阳光。有时候也会有故障。
            </p>
         </div>
         <p>正如你在本章中看到的，<span class="acronym">SOAP</span> 牵扯了很多层面。<span class="acronym">SOAP</span> 向 HTTP 服务器发送 XML 文档并接收返回的 XML 文档时需要用到 HTTP 层。这样一来，你在 <a href="../http_web_services/index.html" title="第&nbsp;11&nbsp;章&nbsp;HTTP Web 服务">第&nbsp;11&nbsp;章 <i>HTTP Web 服务</i></a> 学到的调试技术在这里都有了用武之地。你可以 <b class="userinput"><tt>import httplib</tt></b> 并设置 <b class="userinput"><tt>httplib.HTTPConnection.debuglevel = 1</tt></b> 来查看潜在的 HTTP 传输。
         </p>
         <p>在 HTTP 层之上，还有几个可能发生问题的地方。<span class="application">SOAPpy</span> 隐藏 <span class="acronym">SOAP</span> 语法的本领令你惊叹不已，但也意味着在发生问题时更难确定问题所在。
         </p>
         <p>下面的这些例子是我在使用 <span class="acronym">SOAP</span> 网络服务时犯过的一些常见错误以及所产生的错误信息。
         </p>
         <div class="example"><a name="d0e31792"></a><h3 class="title">例&nbsp;12.15.&nbsp;以错误的设置调用 Proxy 方法</h3><pre class="screen">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput"><span class='pykeyword'>from</span> SOAPpy <span class='pykeyword'>import</span> SOAPProxy</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">url = <span class='pystring'>'http://services.xmethods.net:80/soap/servlet/rpcrouter'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">server = SOAPProxy(url)</span>                                        <a name="soap.troubleshooting.1.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">server.getTemp(<span class='pystring'>'27502'</span>)</span>                                        <a name="soap.troubleshooting.1.2"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12">
<span class="traceback">&lt;Fault SOAP-ENV:Server.BadTargetObjectURI:
Unable to determine object id from call: is the method element namespaced?&gt;
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in ?
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
    return self.__r_call(*args, **kw)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
    self.__hd, self.__ma)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
    raise p
SOAPpy.Types.faultType: &lt;Fault SOAP-ENV:Server.BadTargetObjectURI:
Unable to determine object id from call: is the method element namespaced?&gt;</span>
</pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.1.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">你看出错误了吗？你手工地创建了一个 <tt class="classname">SOAPProxy</tt>，你正确地指定了服务 <span class="acronym">URL</span>，但是你没有指定命名空间。由于多个服务可能被路由到相同的服务 <span class="acronym">URL</span>，命名空间是确定你所调用的服务和方法的重要内容。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.1.2"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">服务器返回的是一个 <span class="acronym">SOAP</span> 错误 (Fault)，<span class="application">SOAPpy</span> 把它转换为 <span class="application">Python</span> 异常 <tt class="classname">SOAPpy.Types.faultType</tt>。从任何 <span class="acronym">SOAP</span> 服务器返回的错误都是 <span class="acronym">SOAP</span> 错误，因此你可以轻易地捕获这个异常。就此处而言，我们能从 <span class="acronym">SOAP</span> 错误信息中看出端倪：由于源 <tt class="classname">SOAPProxy</tt> 对象没有设置服务命名空间，因此方法元素也就没有了命名空间。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
         <p>错误配置 <span class="acronym">SOAP</span> 服务的基本元素是 <span class="acronym">WSDL</span> 着眼解决的问题。<span class="acronym">WSDL</span> 文件包含服务 <span class="acronym">URL</span> 和命名空间，所以你应该不会在这里犯错。但是，还有其他可能出错的地方。
         </p>
         <div class="example"><a name="d0e31878"></a><h3 class="title">例&nbsp;12.16.&nbsp;以错误参数调用方法</h3><pre class="screen">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">wsdlFile = <span class='pystring'>'http://www.xmethods.net/sd/2001/TemperatureService.wsdl'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">server = WSDL.Proxy(wsdlFile)</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">temperature = server.getTemp(27502)</span>                                <a name="soap.troubleshooting.2.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
<span class="traceback">&lt;Fault SOAP-ENV:Server: Exception while handling service request:
services.temperature.TempService.getTemp(int) -- no signature match&gt;   <a name="soap.troubleshooting.2.2"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12">
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in ?
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
    return self.__r_call(*args, **kw)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
    self.__hd, self.__ma)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
    raise p
SOAPpy.Types.faultType: &lt;Fault SOAP-ENV:Server: Exception while handling service request:
services.temperature.TempService.getTemp(int) -- no signature match&gt;</span>
</pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.2.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">你看出错误了吗？这是一个不易察觉的错误：你在使用整数而不是字符串来调用 <tt class="function">server.getTemp</tt> 。自省 <span class="acronym">WSDL</span> 文件不难发现，<tt class="function">getTemp()</tt> 这个 <span class="acronym">SOAP</span> 函数接受一个参数 <tt class="varname">zipcode</tt>，这是一个字符串参数。<tt class="classname">WSDL.Proxy</tt> <span class="emphasis"><em>不</em></span> 会为你强制转换数据类型；你需要根据服务器需要的数据类型传递数据。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.2.2"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">又是这样，服务器传回一个 <span class="acronym">SOAP</span> 错误，你能从 <span class="acronym">SOAP</span> 错误信息中看出端倪：你在使用整数类型的参数调用 <tt class="function">getTemp</tt> 函数，但却没有一个以此命名的函数接收整数参数。理论上讲，<span class="acronym">SOAP</span> 允许你重载 (<span class="emphasis"><em>overload</em></span>) 函数，也就是可以在同一个 <span class="acronym">SOAP</span> 服务中存在同名函数，并且参数个数也相同，但是参数的数据类型不同。这就是数据类型必须匹配的原因，也说明了为什么 <tt class="classname">WSDL.Proxy</tt> 不强制地为你改变数据类型。如果真的强制改变了数据类型，发生这样的错误时，调用的可能是另外一个不相干的函数。看来产生这样的错误是件幸运的事。对于数据类型多加注意会让事情简单很多，一旦搞错了数据类型便立刻会发生错误。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
         <p><span class="application">Python</span> 所期待的返回值个数与远程函数的实际返回值个数不同是另一种可能的错误。
         </p>
         <div class="example"><a name="d0e31958"></a><h3 class="title">例&nbsp;12.17.&nbsp;调用时方法所期待的返回值个数错误</h3><pre class="screen">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">wsdlFile = <span class='pystring'>'http://www.xmethods.net/sd/2001/TemperatureService.wsdl'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">server = WSDL.Proxy(wsdlFile)</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">(city, temperature) = server.getTemp(27502)</span>  <a name="soap.troubleshooting.3.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
<span class="traceback">Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in ?
TypeError: unpack non-sequence</span>
</pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.3.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">你看出错误了吗？<tt class="function">server.getTemp</tt> 只返回一个浮点值，但你写的代码却期待着获得两个值，并把它们赋值给不同的两个变量。注意这不是一个 <span class="acronym">SOAP</span> 错误。就远程服务器而言没有发生任何错误。错误发生在完成 <span class="acronym">SOAP</span> 交割<span class="emphasis"><em>之后</em></span>。<tt class="classname">WSDL.Proxy</tt> 返回一个浮点数，你本地的 <span class="application">Python</span> 解释器试图将这个浮点数分成两个变量。由于函数只返回了一个值，你在试图分割它时所获得的是一个 <span class="application">Python</span> 异常，而不是 <span class="acronym">SOAP</span> 错误。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
         <p>那么 Google 网络服务方面又如何呢？我曾经犯过的最常见的错误是忘记正确设置应用许可证。</p>
         <div class="example"><a name="d0e32013"></a><h3 class="title">例&nbsp;12.18.&nbsp;调用方法返回一个应用特定的错误</h3><pre class="screen">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput"><span class='pykeyword'>from</span> SOAPpy <span class='pykeyword'>import</span> WSDL</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">server = WSDL.Proxy(r<span class='pystring'>'/path/to/local/GoogleSearch.wsdl'</span>)</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">results = server.doGoogleSearch(<span class='pystring'>'foo'</span>, <span class='pystring'>'mark'</span>, 0, 10, False, <span class='pystring'>""</span>,</span> <a name="soap.troubleshooting.4.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
<tt class="prompt">...     </tt><span class="userinput">False, <span class='pystring'>""</span>, <span class='pystring'>"utf-8"</span>, <span class='pystring'>"utf-8"</span>)</span>
<span class="traceback">&lt;Fault SOAP-ENV:Server:                                              <a name="soap.troubleshooting.4.2"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12">
 Exception from service object: Invalid authorization key: foo:
 &lt;SOAPpy.Types.structType detail at 14164616&gt;:
 {'stackTrace':
  'com.google.soap.search.GoogleSearchFault: Invalid authorization key: foo
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:220)
   at com.google.soap.search.QueryLimits.validateKey(QueryLimits.java:127)
   at com.google.soap.search.GoogleSearchService.doPublicMethodChecks(
     GoogleSearchService.java:825)
   at com.google.soap.search.GoogleSearchService.doGoogleSearch(
     GoogleSearchService.java:121)
   at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
   at java.lang.reflect.Method.invoke(Unknown Source)
   at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
   at org.apache.soap.providers.RPCJavaProvider.invoke(
     RPCJavaProvider.java:129)
   at org.apache.soap.server.http.RPCRouterServlet.doPost(
     RPCRouterServlet.java:288)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
   at com.google.gse.HttpConnection.runServlet(HttpConnection.java:237)
   at com.google.gse.HttpConnection.run(HttpConnection.java:195)
   at com.google.gse.DispatchQueue$WorkerThread.run(DispatchQueue.java:201)
Caused by: com.google.soap.search.UserKeyInvalidException: Key was of wrong size.
   at com.google.soap.search.UserKey.&lt;init&gt;(UserKey.java:59)
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:217)
   ... 14 more
'}&gt;
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in ?
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
    return self.__r_call(*args, **kw)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
    self.__hd, self.__ma)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
    raise p
SOAPpy.Types.faultType: &lt;Fault SOAP-ENV:Server: Exception from service object:
Invalid authorization key: foo:
&lt;SOAPpy.Types.structType detail at 14164616&gt;:
{'stackTrace':
  'com.google.soap.search.GoogleSearchFault: Invalid authorization key: foo
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:220)
   at com.google.soap.search.QueryLimits.validateKey(QueryLimits.java:127)
   at com.google.soap.search.GoogleSearchService.doPublicMethodChecks(
     GoogleSearchService.java:825)
   at com.google.soap.search.GoogleSearchService.doGoogleSearch(
     GoogleSearchService.java:121)
   at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
   at java.lang.reflect.Method.invoke(Unknown Source)
   at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
   at org.apache.soap.providers.RPCJavaProvider.invoke(
     RPCJavaProvider.java:129)
   at org.apache.soap.server.http.RPCRouterServlet.doPost(
     RPCRouterServlet.java:288)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
   at com.google.gse.HttpConnection.runServlet(HttpConnection.java:237)
   at com.google.gse.HttpConnection.run(HttpConnection.java:195)
   at com.google.gse.DispatchQueue$WorkerThread.run(DispatchQueue.java:201)
Caused by: com.google.soap.search.UserKeyInvalidException: Key was of wrong size.
   at com.google.soap.search.UserKey.&lt;init&gt;(UserKey.java:59)
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:217)
   ... 14 more
'}&gt;</span>
</pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.4.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">你看出错误了吗？调用的语法，参数个数以及数据类型都没有错误。这个问题是应用特定的：第一个参数应该是我的应用许可证，但 <tt class="literal">foo</tt> 不是一个有效的 Google 许可证。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#soap.troubleshooting.4.2"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">Google 服务器返回的是一个 <span class="acronym">SOAP</span> 错误和一大串特别长的错误信息，其中包含了完整的 Java 堆栈跟踪。记住<span class="emphasis"><em>所有</em></span> 的 <span class="acronym">SOAP</span> 错误都被标示为 <span class="acronym">SOAP</span> Faults: errors in configuration (设置错误), errors in function arguments (函数参数错误)，或者是应用特定的错误 (这里就是) 等等。在其中埋藏的至关重要信息是：<tt class="literal">Invalid authorization key: foo</tt> (非有效授权许可证：foo)。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
         <div class="furtherreading">
            <h3>进一步阅读</h3>
            <ul>
               <li><a href="http://www-106.ibm.com/developerworks/webservices/library/ws-pyth17.html">New developments for <span class="application">SOAPpy</span></a> 一步步连接到另一个不名副其实的 <span class="acronym">SOAP</span> 服务。
               </li>
            </ul>
         </div>
      </div>
      <table class="Footer" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td width="35%" align="left"><br><a class="NavigationArrow" href="google.html">&lt;&lt;&nbsp;搜索 Google</a></td>
            <td width="30%" align="center"><br>&nbsp;<span class="divider">|</span>&nbsp;<a href="index.html#soap.divein" title="12.1.&nbsp;概览">1</a> <span class="divider">|</span> <a href="install.html" title="12.2.&nbsp;安装 SOAP 库">2</a> <span class="divider">|</span> <a href="first_steps.html" title="12.3.&nbsp;步入 SOAP">3</a> <span class="divider">|</span> <a href="debugging.html" title="12.4.&nbsp; SOAP 网络服务查错">4</a> <span class="divider">|</span> <a href="wsdl.html" title="12.5.&nbsp;WSDL 介绍">5</a> <span class="divider">|</span> <a href="introspection.html" title="12.6.&nbsp;以 WSDL 进行 SOAP 内省">6</a> <span class="divider">|</span> <a href="google.html" title="12.7.&nbsp;搜索 Google">7</a> <span class="divider">|</span> <span class="thispage">8</span> <span class="divider">|</span> <a href="summary.html" title="12.9.&nbsp;小结">9</a>&nbsp;<span class="divider">|</span>&nbsp;
            </td>
            <td width="35%" align="right"><br><a class="NavigationArrow" href="summary.html">小结&nbsp;&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3"><br></td>
         </tr>
      </table>
      <div class="Footer">
         <p class="copyright">Copyright © 2000, 2001, 2002, 2003, 2004 <a href="mailto:mark@diveintopython.org">Mark Pilgrim</a></p>
         <p class="copyright">Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007 <a href="mailto:python-cn@googlegroups.com">CPyUG (邮件列表)</a></p>
      </div>
   </body>
</html>